import { createFiber } from "./createFiber";
import { isArray, isStringOrNumber, Update, Placement } from "./utils";

/**
 * diff核心算法
 * 对比新老vdom并将操作记录下来
 */

// fiber节点的删除
// 在returnFiber节点上创建一个需要被删除的节点的集合
function deleteChild(returnFiber, deleteToChild) {
  if (returnFiber.deletions) {
    returnFiber.deletions.push(deleteToChild)
  }
  else {
    returnFiber.deletions = [deleteToChild]
  }
}

// diff算法
// 删除节点链表,头结点是currentFirstChild
function deleteRemainingChildren(returnFiber, currentFirstChild) {
  let childToDelete = currentFirstChild;
  while (childToDelete) {
    deleteChild(returnFiber, childToDelete);
    childToDelete = childToDelete.sibling;
  }
}

// fiber节点的比对
function sameNode(a, b) {
  return !!(a && b && a.key === b.key && a.type && b.type);
}
function placeChild(
  newFiber,
  lastPlacedIndex,
  newIndex,
  shouldTrackSideEffects // 初次渲染（false）还是更新（true）
) {
  newFiber.index = newIndex;
  if (!shouldTrackSideEffects) {
    return lastPlacedIndex;
  }
  const current = newFiber.alternate;

  if (current) {
    const oldIndex = current.index;
    if (oldIndex < lastPlacedIndex) {
      // move
      newFiber.flags = Placement;
      return lastPlacedIndex;
    } else {
      return oldIndex;
    }
  } else {
    newFiber.flags = Placement;
    return lastPlacedIndex;
  }
}
function mapRemainingChildren(currentFirstChild) {
  const existingChildren = new Map();
  let existingChild = currentFirstChild;
  while (existingChild) {
    existingChildren.set(
      existingChild.key || existingChild.index,
      existingChild
    );
    existingChild = existingChild.sibling;
  }
  return existingChildren;
}
export function reconcileChildren(returnFiber, children) {
  if (isStringOrNumber(children)) {
    return;
  }
  const newChildren = isArray(children) ? children : [children];
  // 判断是渲染（false）还是更新（true）
  const shouldTrackSideEffects = !!returnFiber.alternate

  let previosNewFiber = null;
  // 添加oldFiber节点更新
  let oldFiber = returnFiber.alternate && returnFiber.alternate.child;
  // 记录上次节点插入的位置
  let lastPlacedIndex = 0;
  let newIdx = 0;
  // 记录下一个oldFiber节点
  let nextOldFiber = null

  // 从头遍历新节点找到第一个不可复用的节点（替换）
  for (; oldFiber != null && newIdx < newChildren.length; newIdx++) {
    const newChild = newChildren[newIdx];
    if (newChild == null) {
      continue;
    }
    if (oldFiber.index > newIdx) {
      // 说明节点有位置变化
      nextOldFiber = oldFiber;
      oldFiber = null
    } else {
      nextOldFiber = oldFiber.sibling
    }

    const same = sameNode(newChild, oldFiber)
    if (!same) {
      if (oldFiber == null) {
        oldFiber = nextOldFiber;
      }
      break;
    }
    // 可复用
    const newFiber = createFiber(newChild, returnFiber)

    Object.assign(newFiber, {
      alternate: oldFiber,
      stateNode: oldFiber.stateNode,
      flags: Update
    })
    lastPlacedIndex = placeChild(
      newFiber,
      lastPlacedIndex,
      newIdx,
      shouldTrackSideEffects
    )
    if (previosNewFiber === null) {
      returnFiber.child = newFiber;
    } else {
      previosNewFiber.sibling = newFiber;
    }
    previosNewFiber = newFiber;
    // !切换
    oldFiber = nextOldFiber;
  }

  // old 1 2 3 4 5 6 7
  // new 0 1 2 3
  // 如果新节点全都遍历完成 这直接删除老节点中未遍历到节点
  if (newIdx === newChildren.length) {
    deleteRemainingChildren(returnFiber, oldFiber);
    return;
  }

  // old 1 2 3 4
  // new 1 2 3 4 5 6 7
  // 初次渲染或者追加节点
  if (oldFiber == null) {
    for (; newIdx < newChildren.length; newIdx++) {
      const newChild = newChildren[newIdx];
      if (newChild === null) {
        continue
      }
      const newFiber = createFiber(newChild, returnFiber);
      lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx, shouldTrackSideEffects)
      if (previosNewFiber === null) {
        returnFiber.child = newFiber;
      } else {
        previosNewFiber.sibling = newFiber;
      }

      previosNewFiber = newFiber;
    }
  }

  // old  0 1 2 | 3 4  fiber ->map{key: value}
  // new  0 1 2 | 5 4 3 8 newChildren[]
  // 现将剩余的oldFiber打成一个map
  const existingChildren = mapRemainingChildren(oldFiber);
  for (; newIdx < newChildren.length; newIdx++) {

    const newChild = newChildren[newIdx];
    if (newChild === null) {
      continue
    }
    const newFiber = createFiber(newChild, returnFiber);

    lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx, shouldTrackSideEffects)

    // existingChildren查询可复用节点
    const matchedFiber = existingChildren.get(newFiber.key || newFiber.index)
    if (matchedFiber) {
      // 找到一个就删除一个那么最后剩下的直接删除就可以了
      existingChildren.delete(newFiber.key || newFiber.index)
      Object.assign(newFiber, {
        alternate: matchedFiber,
        stateNode: matchedFiber.stateNode,
        flags: Update
      })
    }

    if (previosNewFiber === null) {
      returnFiber.child = newFiber
    } else {
      previosNewFiber.sibling = newFiber
    }
    previosNewFiber = newFiber;
  }

  // 将剩余的oldFiber节点删除
  if (shouldTrackSideEffects) {
    existingChildren.forEach(child => deleteChild(returnFiber, child))
  }
}

